package api

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/sirupsen/logrus"
	"gorm.io/gorm"
	"hcy-api/lib/http"
	"hcy-api/lib/id"
	"hcy-api/lib/utils"
	"hcy-api/structs/enum"
	"hcy-api/structs/sys"
	"hcy-api/structs/tables"
	http2 "net/http"
	"sys/global"
	"sys/module/dao"
	"sys/module/service"
	"time"
)

type userApi struct {
}

var UserApi = new(userApi)

func (api userApi) GetUserList(c *gin.Context) {
	self := http.GetCurrentUser(c)
	li, err := service.UserServ.GetUserList(self.UserID, self.AccountLv)
	if err != nil {
		http.BadWithDB(c, err)
	}
	http.Ok(c, li)
}

func (api userApi) Login(c *gin.Context) {
	var loginAccount sys.LoginAccount
	if err := c.ShouldBindJSON(&loginAccount); err != nil {
		logrus.Error(err)
		http.BadWithParamsError(c, err)
		return
	}
	if !service.UserServ.VerifyCode(loginAccount.VeryCode) {
		http.BadWithCode(c, enum.ErrorMap.VerifyCodeErr)
		return
	}
	result, _, err := service.UserServ.UserLogin(loginAccount)
	if err != nil {
		c.AbortWithStatusJSON(http2.StatusBadRequest, gin.H{"success": false, "data": err.Error()})
		return
	}
	http.Ok(c, gin.H{"success": true, "data": result})
}

func (api userApi) GetRouterTreeForFile(c *gin.Context) {
	result, err := service.UserServ.GetRouterTreeForFile()
	if err != nil {
		c.AbortWithStatusJSON(http2.StatusBadRequest, gin.H{"success": false, "data": err.Error()})
		return
	}
	c.Data(http2.StatusOK, "application/json", result)
}

func (api userApi) Add(c *gin.Context) {
	var in sys.AddUserForm
	if err := c.ShouldBindJSON(&in); err != nil {
		http.BadWithParamsError(c, err)
		return
	}
	if in.Nickname == "" {
		in.Nickname = in.Username
	}
	if in.Password == "" {
		in.Password = tables.DefaultPassword
	}
	_, b := dao.UserDB.UsernameExist(in.Username)
	if b {
		http.BadWithCode(c, enum.ErrorMap.AccountIsExist)
		return
	}
	self := http.GetCurrentUser(c)
	uid := id.GetSnowId()
	err := global.DB.Transaction(func(tx *gorm.DB) error {
		user := &tables.User{
			UserId:    uid,
			Username:  in.Username,
			Password:  in.Password,
			Status:    in.Status,
			IsOnline:  2,
			AccountLv: in.AccountLv,
			OrgId:     in.OrgId,
			Extend: tables.UserExtend{
				Id:       id.GetSnowId(),
				UserId:   uid,
				Nickname: in.Nickname,
				Email:    in.Email,
				CityCode: in.CityCode,
				AutoCreateInfo: tables.AutoCreateInfo{
					Creator:   self.Username,
					CreatorId: self.OrgId,
					CreatedAt: time.Now().Unix(),
					UpdatedAt: 0,
					DeletedAt: 0,
				},
			},
			AutoCreateInfo: tables.AutoCreateInfo{
				Creator:   self.Username,
				CreatorId: self.OrgId,
				CreatedAt: time.Now().Unix(),
				UpdatedAt: 0,
				DeletedAt: 0,
			},
		}
		err := tx.Create(user).Error
		if err != nil {
			return err
		}
		mrs := make([]tables.MapUserRole, 0)
		for _, v := range in.RoleIds {
			mrs = append(mrs, tables.MapUserRole{
				UserId: uid,
				RoleId: v,
			})
		}
		err = tx.CreateInBatches(mrs, len(mrs)).Error
		return err
	})
	if err != nil {
		http.BadWithDB(c, err)
		return
	}
	err = service.UserServ.CallVapForAddWinUser(fmt.Sprintf("%d", uid))
	if err != nil {
		http.BadWithDB(c, err)
		return
	}
	http.OK204(c)
}

func (api userApi) Update(c *gin.Context) {
	var in sys.UpdateUserInfoForm
	if err := c.ShouldBindJSON(&in); err != nil {
		http.BadWithParamsError(c, err)
		return
	}
	du, err := dao.UserDB.FindById(in.UserId)
	if err != nil {
		http.BadWithDB(c, err)
		return
	}
	self := http.GetCurrentUser(c)
	if !service.UserServ.CheckUpdateUserAuthority(du, self) {
		http.BadWithCode(c, enum.ErrorMap.AccessDeniedForCreate)
		return
	}
	err = global.DB.Transaction(func(tx *gorm.DB) error {
		du.Status = in.Status
		du.AccountLv = in.AccountLv
		du.OrgId = in.OrgId
		err = tx.Save(du).Error
		if err != nil {
			return err
		}
		var eu tables.UserExtend
		err = tx.Model(&tables.UserExtend{}).Where(&tables.UserExtend{UserId: in.UserId}).Find(&eu).Error
		if err != nil {
			return err
		}
		eu.Nickname = in.Nickname
		eu.Email = in.Email
		eu.CityCode = in.CityCode
		err = tx.Save(&eu).Error
		return err
	})
	if err != nil {
		http.BadWithDB(c, err)
		return
	}
	http.OK204(c)
}

func (api userApi) UpdatePassword(c *gin.Context) {
	var in sys.UpdatePasswordForm
	if err := c.ShouldBindJSON(&in); err != nil {
		http.BadWithParamsError(c, err)
		return
	}
	self := http.GetCurrentUser(c)
	du, err := dao.UserDB.FindById(self.UserID)
	if err != nil {
		http.BadWithDB(c, err)
		return
	}
	if du.Password != in.OldPassword {
		http.BadWithCode(c, enum.ErrorMap.OldPasswordError)
		return
	}
	err = global.DB.Transaction(func(tx *gorm.DB) error {
		du.Password = in.NewPassword
		return tx.Save(du).Error
	})
	if err != nil {
		http.BadWithDB(c, err)
		return
	}
	http.OK204(c)
}

func (api userApi) ResetPassword(c *gin.Context) {
	in := struct {
		Id int64 `uri:"id"`
	}{}
	if err := c.ShouldBindUri(&in); err != nil {
		http.BadWithParamsError(c, err)
		return
	}
	du, err := dao.UserDB.FindById(in.Id)
	if err != nil {
		http.BadWithDB(c, err)
		return
	}
	self := http.GetCurrentUser(c)
	if !service.UserServ.CheckUpdateUserAuthority(du, self) {
		http.BadWithCode(c, enum.ErrorMap.AccessDeniedForCreate)
		return
	}
	du.Password = tables.DefaultPassword
	if err = dao.UserDB.UpdateById(du); err != nil {
		http.BadWithDB(c, err)
		return
	}
	http.OK204(c)
}

func (api userApi) Delete(c *gin.Context) {
	in := struct {
		Id int64 `uri:"id"`
	}{}
	if err := c.ShouldBindUri(&in); err != nil {
		http.BadWithParamsError(c, err)
		return
	}
	du, err := dao.UserDB.FindById(in.Id)
	if err != nil {
		http.BadWithDB(c, err)
		return
	}
	self := http.GetCurrentUser(c)
	if !service.UserServ.CheckUpdateUserAuthority(du, self) {
		http.BadWithCode(c, enum.ErrorMap.AccessDeniedForCreate)
		return
	}
	err = global.DB.Transaction(func(tx *gorm.DB) error {
		err = tx.Where(&tables.User{UserId: in.Id}).Delete(&tables.User{}).Error
		if err != nil {
			return err
		}
		err = tx.Where(&tables.UserExtend{UserId: in.Id}).Delete(&tables.UserExtend{}).Error
		if err != nil {
			return err
		}
		// 删除用户角色映射
		err = tx.Where(&tables.MapUserRole{UserId: in.Id}).Delete(&tables.MapUserRole{}).Error
		if err != nil {
			return err
		}
		//删除用户课程映射
		//err = tx.Where(&tables.MapCourse2User{UserId: in.Id}).Delete(&tables.MapCourse2User{}).Error
		return err
	})
	if err != nil {
		http.BadWithDB(c, err)
		return
	}
	http.OK204(c)
}

func (api userApi) Page(c *gin.Context) {
	var in sys.SearchUser
	if err := c.ShouldBindQuery(&in); err != nil {
		http.BadWithParamsError(c, err)
		return
	}
	self := http.GetCurrentUser(c)
	li, total, err := dao.UserDB.FindPage(in, self.UserID)
	if err != nil {
		http.BadWithDB(c, err)
		return
	}
	dtos := make([]sys.UserDto, len(li))
	m := service.OrgService.GetOrgMap()
	for i, v := range li {
		dtos[i] = sys.UserDto{
			User: v,
		}
		if org, ok := m[v.OrgId]; ok {
			dtos[i].OrgName = org.OrgName
		}
	}
	http.OkPage(c, dtos, total)
}

func (api userApi) BindOrg(c *gin.Context) {
	var in sys.UserBindOrg
	if err := c.ShouldBindJSON(&in); err != nil {
		http.BadWithParamsError(c, err)
		return
	}
	err := global.DB.Transaction(func(tx *gorm.DB) error {
		err := tx.Where(&tables.MapUserOrg{UserId: in.UserId}).Delete(&tables.MapUserOrg{}).Error
		if err != nil {
			return err
		}
		li := make([]tables.MapUserOrg, 0)
		for _, v := range in.OrgIds {
			li = append(li, tables.MapUserOrg{
				UserId: in.UserId,
				OrgId:  v,
			})
		}
		if err = tx.CreateInBatches(&li, len(li)).Error; err != nil {
			return err
		}
		return nil
	})
	if err != nil {
		http.BadWithDB(c, err)
		return
	}
	http.OK204(c)
}

func (api userApi) BindRole(c *gin.Context) {
	var in sys.UserBindRole
	if err := c.ShouldBindJSON(&in); err != nil {
		http.BadWithParamsError(c, err)
		return
	}
	err := global.DB.Transaction(func(tx *gorm.DB) error {
		err := tx.Where(&tables.MapUserRole{UserId: in.UserId}).Delete(&tables.MapUserRole{}).Error
		if err != nil {
			return err
		}
		li := make([]tables.MapUserRole, 0)
		for _, v := range in.RoleIds {
			li = append(li, tables.MapUserRole{
				UserId: in.UserId,
				RoleId: v,
			})
		}
		if err = tx.CreateInBatches(&li, len(li)).Error; err != nil {
			return err
		}
		return nil
	})
	if err != nil {
		http.BadWithDB(c, err)
		return
	}
	http.OK204(c)
}

func (api userApi) GetBoundOrg(c *gin.Context) {
	in := struct {
		Id int64 `uri:"id"`
	}{}
	if err := c.ShouldBindUri(&in); err != nil {
		http.BadWithParamsError(c, err)
		return
	}
	ids, err := dao.UserDB.FindOrgIds(in.Id)
	if err != nil {
		http.BadWithDB(c, err)
		return
	}
	idsStr := utils.StringArrayInt64(ids)
	http.Ok(c, idsStr)
}

func (api userApi) GetBoundRole(c *gin.Context) {
	in := struct {
		Id int64 `uri:"id"`
	}{}
	if err := c.ShouldBindUri(&in); err != nil {
		http.BadWithParamsError(c, err)
		return
	}
	ids, err := dao.UserDB.FindRoleIds(in.Id)
	if err != nil {
		http.BadWithDB(c, err)
		return
	}
	idsStr := utils.StringArrayInt64(ids)
	http.Ok(c, idsStr)
}
